/**
 * @file mmu.h
 * @author lcz (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-10-28
 *
 * @copyright Copyright (c) 2022
 *
 */

#ifndef MMU_H
#define MMU_H
/**
 * 仅作为头文件，没有.c文件
 * */
#include "comm/types.h"
#include "comm/cpu_instr.h"

#define PDE_CNT 1024
#define PTE_CNT 1024
#define PDE_P (1 << 0)
#define PTE_P (1 << 0)

#define PDE_W (1 << 1)
#define PTE_W (1 << 1)
#define PDE_U (1 << 2)
#define PTE_U (1 << 2)



#pragma pack(1)
/**
 * @brief 目录页结构体
 *
 */
typedef union _pde_t
{
    uint32_t v; // 32位值
    // 可以访问全部值，也可以访问所有比特位
    struct
    {
        uint32_t present : 1;       // 0 (P) Present; must be 1 to map a 4-KByte page
        uint32_t write_enable : 1;  // 1 (R/W) Read/write, if 0, writes may not be allowe
        uint32_t user_mode_acc : 1; // 2 (U/S) if 0, user-mode accesses are not allowed t
        uint32_t write_through : 1; // 3 (PWT) Page-level write-through
        uint32_t cache_disable : 1; // 4 (PCD) Page-level cache disable
        uint32_t accessed : 1;      // 5 (A) Accessed
        uint32_t : 1;               // 没有用到的1位// 6 Ignored;
        uint32_t ps : 1;            // 7 (PS)
        uint32_t : 4;               // 没有用到的4位，到此偏移12位用完// 11:8 Ignored
        // 物理地址
        uint32_t phy_pt_addr : 20; // 高20位物理地址
    };
} pde_t;

// 2）定义页表结构体
typedef union _pte_t
{
    uint32_t v; // 32位值
    // 可以访问全部值，也可以访问所有比特位
    struct
    {
        uint32_t present : 1;       // 0 (P) Present; must be 1 to map a 4-KByte page
        uint32_t write_enable : 1;  // 1 (R/W) Read/write, if 0, writes may not be allowe
        uint32_t user_mode_acc : 1; // 2 (U/S) if 0, user-mode accesses are not allowed t
        uint32_t write_through : 1; // 3 (PWT) Page-level write-through
        uint32_t cache_disable : 1; // 4 (PCD) Page-level cache disable
        uint32_t accessed : 1;      // 5 (A) Accessed;
        uint32_t dirty : 1;         // 没有用到的1位// 7 PAT
        uint32_t pat : 1;           // 7 PAT
        uint32_t global : 1;        // 8 (G) Global
        uint32_t : 3;               // 没有用到的3位，到此偏移12位用完
        // 物理地址
        uint32_t phy_page_addr : 20;
    };
} pte_t;

#pragma pack()
/**
 * @brief 更新页目录
 *
 * @param paddr 页目录
 * @return * void
 */
static inline void mmu_set_page_dir(uint32_t paddr)
{
    write_cr3(paddr);
}

// declare function
/**
 * @brief 注意页目录的地址是放在cr3中的，线性地址高10位放的是页目录中的索引，所以此处返回索引没有问题的
 *
 * @param vaddr 线性地址
 * @return uint32_t 线性地址高10位页目录中的索引
 */
static inline uint32_t pde_index(uint32_t vaddr)
{
    int index = (vaddr) >> 22;
    return index;
}

/**
 * @brief 高10位清除,实际便是取线性地址中间10位
 * 
 * @param vaddr 
 * @return * uint32_t 
 */
static inline uint32_t pte_index(uint32_t vaddr)
{
    return (vaddr >> 12) & 0x3ff;
}
/**
 * @brief 输入的是一级页目录项，返回二级页表项的地址
 * 
 * @param pde 
 * @return * uint32_t 
 */
static inline uint32_t pde_paddr(pde_t *pde)
{
    // phy_pt_addr 页目录表项高20位地址左移12位，便就是32位的pte地址，返回
    return pde->phy_pt_addr << 12;
}
/**
 * @brief 输入的是二级页表项，放回物理页的地址
 *
 * @param pte
 * @return * uint32_t
 */
static inline uint32_t pte_paddr(pte_t *pte)
{
    // phy_page_addr 高20位的地址左移12位，便就是32位的物理页地址地址，返回
    return pte->phy_page_addr << 12;
}

/**
 * @brief Get the pte perm （获取pte的权限字段）
 *
 * @param pte pte表项
 * @return * uint32_t 权限
 */
static inline uint32_t get_pte_perm(pte_t *pte)
{
    // 返回前10位
    return (pte->v & 0xfff);
}
#endif
